#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_err.h"

#include "nvs_manager.h"

static const char* TAG = "NVS";

// 设备信息缓存
static nv_device_info_t g_device_info_cache;

/**
 * @brief 获取设备信息缓存
 * @return 设备信息缓存
 */
nv_device_info_t *get_device_info_cache(void)
{
    return &g_device_info_cache;
} 

/**
 * @brief 设置设备信息缓存数据
 * @param key 设备秘钥
 * @param data 信息数据指针
 */
static void set_device_info_cache_data(const char* key, void* data)
{
    if (strstr(key, NVS_WIFI_INFO_KEY)) {
        nv_wifi_info_t* wifi_info = (nv_wifi_info_t *)data;
        memcpy(&g_device_info_cache.wifi_info,      wifi_info,      sizeof(nv_wifi_info_t));
        g_device_info_cache.wifi_info_cache_valid = true;
    }
    else if (strstr(key, NVS_ALIOT_INFO_KEY)) {
        nv_aliot_info_t* aliot_info = (nv_aliot_info_t *)data;
        memcpy(&g_device_info_cache.aliot_info,     aliot_info,     sizeof(nv_aliot_info_t));
        g_device_info_cache.aliot_info_cache_valid = true;
    }
}

/**
 * @brief 获取设备信息缓存数据
 * @param key 设备秘钥
 * @param data 信息数据指针
 * @return 错误码
 */
static int get_device_info_cache_data(const char* key, void* data)
{
    if (strstr(key, NVS_WIFI_INFO_KEY) && g_device_info_cache.wifi_info_cache_valid) {
        nv_wifi_info_t* wifi_info = (nv_wifi_info_t *)data;
        memcpy(wifi_info,   &g_device_info_cache.wifi_info,     sizeof(nv_wifi_info_t));

        return ESP_OK;
    }
    else if (strstr(key, NVS_ALIOT_INFO_KEY) && g_device_info_cache.aliot_info_cache_valid) {
        nv_aliot_info_t* aliot_info = (nv_aliot_info_t *)data;
        memcpy(aliot_info,  &g_device_info_cache.aliot_info,    sizeof(nv_aliot_info_t));

        return ESP_OK;
    }

    return ESP_FAIL;
}

/**
 * @brief 保存驱动信息到NV存储
 * @param key 设备秘钥
 * @param data 信息数据指针
 * @param size 信息数据大小
 * @return 错误码
 */
esp_err_t nvs_device_info_save(const char* key, void* data, size_t size)
{
    if (!strstr(key, NVS_WIFI_INFO_KEY) || !strstr(key, NVS_ALIOT_INFO_KEY) || !data) {
        ESP_LOGI(TAG, "%s key param fail!", __func__);
        return ESP_FAIL;
    }

    nvs_handle_t nvs_handle;
    esp_err_t ret;

    // 打开命名空间
    ESP_ERROR_CHECK(nvs_open(NVS_DEVICE_INFO_NAMESPACE, NVS_READWRITE, &nvs_handle));

    // 写入保存的结构体
    ESP_LOGI(TAG, "%s device_info_size:%d", __func__, size);
    ret = nvs_set_blob(nvs_handle, key, data, size);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "nvs_set_blob failed: %s", esp_err_to_name(ret));
        nvs_close(nvs_handle);
        return ret;
    }

    // 提交更改
    ret = nvs_commit(nvs_handle);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "nvs_commit failed: %s", esp_err_to_name(ret));
    }
    nvs_close(nvs_handle);

    // 将文件写入g_device_info_cache中，下次就不用深入读取了
    set_device_info_cache_data(key, data);

    return ret;
    
}

/**
 * @brief 从NV存储中读取驱动信息
 * @param key 设备秘钥
 * @param data 信息数据指针
 * @param size 信息数据大小
 * @return 错误码
 */
esp_err_t nvs_device_info_read(const char* key, void* data)
{
    // 如果数据已经在g_device_info_cache中就不用再读取了
    if (!strstr(key, NVS_WIFI_INFO_KEY) || !strstr(key, NVS_ALIOT_INFO_KEY) || !data) {
        ESP_LOGI(TAG, "%s key param fail!", __func__);
        return ESP_FAIL;
    }
    else {
        int ret = get_device_info_cache_data(key, data);
        if (ret == ESP_OK) return ret;
    }

    nvs_handle_t nvs_handle;
    esp_err_t ret = ESP_FAIL;
    size_t required_size = 0;

    ESP_ERROR_CHECK(nvs_open(NVS_DEVICE_INFO_NAMESPACE, NVS_READWRITE, &nvs_handle));

    // 获取实际数据大小
    ret = nvs_get_blob(nvs_handle, key, NULL, &required_size);
    ESP_LOGI(TAG, "%s device_info_size:%d", __func__, required_size);
    if (ret != ESP_OK) {
        ESP_LOGI(TAG, "%s get %s size fail!", __func__, key);
        nvs_close(nvs_handle);
        return ret;
    }

    // 读取完整数据
    ret = nvs_get_blob(nvs_handle, key, data, &required_size);
    if (ret != ESP_OK) {
        ESP_LOGI(TAG, "%s get %s fail!", __func__, key);
        nvs_close(nvs_handle);
        return ret;
    }
    nvs_close(nvs_handle);

    // 读取成功后更新到全局变量
    set_device_info_cache_data(key, data);

    return ret;
}


/**
 * @brief nvs存储初始化
 * @return esp_err_t 错误码
 */
esp_err_t nvs_manager_init(void)
{
    nv_wifi_info_t wifi_info = {0};
    nv_aliot_info_t aliot_info = {0};
    esp_err_t err = ESP_FAIL;

    // 初始化 NVS
    err = nvs_flash_init();
    if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ESP_ERROR_CHECK(nvs_flash_init());
    }
    
    // 读取 NVS 中的 wifi 信息
    err = nvs_device_info_read(NVS_WIFI_INFO_KEY, &wifi_info);
    if (err == ESP_OK) {
        ESP_LOGI(TAG, "ssid:%s\tpassword:%s", wifi_info.ssid, wifi_info.password);
        ESP_LOGI(TAG, "Default wifi initialized successfully");
        goto WIFI_INFO;   // 如果读取成功，就不用进行写入数据，直接读取aliot的信息
    }
    
    // 写入 NVS 中默认的设备信息
    strncpy(wifi_info.ssid,             NVS_WIFI_SSID,              NVS_WIFI_SSID_MAX_LEN - 1);
    strncpy(wifi_info.password,         NVS_WIFI_PASSWORD,          NVS_WIFI_PASSWORD_MAX_LEN - 1);

    // 确保字符串都以null结尾
    wifi_info.ssid[NVS_WIFI_SSID_MAX_LEN - 1] = '\0';
    wifi_info.password[NVS_WIFI_PASSWORD_MAX_LEN - 1] = '\0';

    err = nvs_device_info_save(NVS_WIFI_INFO_KEY, &wifi_info, sizeof(nv_wifi_info_t));
    if (err == ESP_OK) {
        ESP_LOGI(TAG, "Default WIFI initialized successfully");
    }
    else {
        ESP_LOGI(TAG, "Failed to initialize default, err=%d", err);
        memset(&wifi_info, 0, sizeof(wifi_info));

        return err;
    }
WIFI_INFO:

    // 读取 NVS 中的 aliot 信息
    err = nvs_device_info_read(NVS_ALIOT_INFO_KEY, &aliot_info);
    if (err == ESP_OK) {
        ESP_LOGI(TAG, "device_name:%s",     aliot_info.device_name);
        ESP_LOGI(TAG, "region_id:%s",       aliot_info.region_id);
        ESP_LOGI(TAG, "product_key:%s",     aliot_info.product_key);
        ESP_LOGI(TAG, "product_secret:%s",  aliot_info.product_secret);
        ESP_LOGI(TAG, "device_secret:%s",   aliot_info.device_secret);
        ESP_LOGI(TAG, "Default wifi initialized successfully");
        goto ALIOT_INFO;
    }
    
    // 写入 NVS 中默认的设备信息
    strncpy(aliot_info.device_name,     NVS_ALIOT_DEVICE_NAME,      NVS_ALIOT_DEVICE_NAME_MAX_LEN - 1);
    strncpy(aliot_info.region_id,       NVS_ALIOT_REGION_ID,        NVS_ALIOT_REGION_ID_MAX_LEN - 1);
    strncpy(aliot_info.product_key,     NVS_ALIOT_PRODUCT_KEY,      NVS_ALIOT_PRODUCT_KEY_MAX_LEN - 1);
    strncpy(aliot_info.product_secret,  NVS_ALIOT_PRODUCT_SECRET,   NVS_ALIOT_PRODUCT_SECRET_MAX_LEN - 1);
    strncpy(aliot_info.device_secret,   NVS_ALIOT_DEVICE_SECRET,    NVS_ALIOT_DEVICE_SECRET_MAX_LEN - 1);

    // 确保字符串都以null结尾
    aliot_info.device_name[NVS_ALIOT_DEVICE_NAME_MAX_LEN - 1] = '\0';
    aliot_info.region_id[NVS_ALIOT_REGION_ID_MAX_LEN - 1] = '\0';
    aliot_info.product_key[NVS_ALIOT_PRODUCT_KEY_MAX_LEN - 1] = '\0';
    aliot_info.product_secret[NVS_ALIOT_PRODUCT_SECRET_MAX_LEN - 1] = '\0';
    aliot_info.device_secret[NVS_ALIOT_DEVICE_SECRET_MAX_LEN - 1] = '\0';

    err = nvs_device_info_save(NVS_ALIOT_INFO_KEY, &aliot_info, sizeof(nv_aliot_info_t));
    if (err == ESP_OK) {
        ESP_LOGI(TAG, "Default ALIOT initialized successfully");
    }
    else {
        ESP_LOGI(TAG, "Failed to initialize default, err=%d", err);
        memset(&aliot_info, 0, sizeof(aliot_info));

        return err;
    }
ALIOT_INFO:

    return err;
}

